package com.common.web.service;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.common.api.exception.CommonException;
import com.common.web.error.TokenError;
import com.common.web.util.WebConstant;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultClaims;
import lombok.extern.slf4j.Slf4j;

/**
 * @Author: liu_chao
 * @Date: 2019/11/11 22:05
 */
@Component
@Slf4j
public class JwtTokenUtil {

	private Map<String, Claims> secretMap = new HashMap<String, Claims>();

	private Map<String,List<String>> authoriyMap = new HashMap<>();
	
	@Value("${jwt.secret:abcefg}")
	private String secret;

	@Value("${jwt.expiration:86400}")
	private Long expiration;
	
	/**
	 * 获取权限集合
	 * @param token
	 * @return
	 */
	public List<String> getAuthorityList(String token){
		return authoriyMap.get(token);
	}
	
	/**
	 * 设置权限集合
	 * @param tokent
	 * @param authorityList
	 */
	public void setAuthority(String tokent,List<String> authorityList) {
		authoriyMap.put(tokent, authorityList);
	}
	
	/**
	 * 根据用户信息生成token
	 */
	public String generateToken(String userCode,String userName) {
		Claims claims = new DefaultClaims();
		claims.setId(userCode);
		claims.setSubject(userName);
		claims.setExpiration(generateExpirationDate());
		String token = generateToken(claims);
		secretMap.put(token, claims);
		return token;
	}

	/**
	 * 从token中获取登录用户编码
	 */
	public String getUserCode(HttpServletRequest request) {
		String token = request.getHeader(WebConstant.TOKEN_KEY);
		if(StringUtils.isEmpty(token)) {
			throw new CommonException(TokenError.tokenNotExist);
		}
		Claims claims =getClaimsFromToken(token);;
		return claims.getId();
	}

	/**
	 * 从token中获取登录用户名称
	 */
	public String getUserName(HttpServletRequest request) {
		String token = request.getHeader(WebConstant.TOKEN_KEY);
		if(StringUtils.isEmpty(token)) {
			throw new CommonException(TokenError.tokenNotExist);
		}
		Claims claims = getClaimsFromToken(token);;
		return claims.getSubject();
	}
	
	/**
	 * 从token中获取登录用户编码和名称
	 */
	public String[] getUserCodeAndName(HttpServletRequest request) {
		String token = request.getHeader(WebConstant.TOKEN_KEY);
		if(StringUtils.isEmpty(token)) {
			throw new CommonException(TokenError.tokenNotExist);
		}
		Claims claims =getClaimsFromToken(token);;
		return new String[] {claims.getId(),claims.getSubject()};
	}
	
	
	/**
	 * 验证token是否还有效
	 *
	 * @param token 客户端传入的token
	 */
	public void validateToken(HttpServletRequest request) {
		String token = request.getHeader(WebConstant.TOKEN_KEY);
		// token为空， 并且GET方式，从请求头中尝试获取token
		if (StringUtils.isEmpty(token) && "GET".equals(request.getMethod())) {
			token = request.getParameter(WebConstant.TOKEN_KEY);
		}
		if (StringUtils.isEmpty(token)) {
			throw new CommonException(TokenError.tokenNotExist);
		}
		Claims claims =  getClaimsFromToken(token);
		Date expiredDate = claims.getExpiration();
		if (expiredDate.after(new Date())) {
			claims.setExpiration(generateExpirationDate());
			secretMap.put(token, claims);
			return ;
		}
		secretMap.remove(token);
		authoriyMap.remove(token);
		throw new CommonException(TokenError.tokenError);

	}

	public void cleanToken(HttpServletRequest request) {
		String token = request.getHeader(WebConstant.TOKEN_KEY);
		if (StringUtils.isEmpty(token)) {
			return ;
		}
		Claims claims=getClaimsFromToken(token);
		if(claims!=null) {
			Date expiredDate = claims.getExpiration();
			if (expiredDate.after(new Date())) {
				// 下次再用可以根据过期时间判断失效，否则无法判断失效
				claims.setExpiration(new Date(0L));
				secretMap.put(token, claims);
			}else {
				secretMap.remove(token);
				authoriyMap.remove(token);
			}
		}
	}

	public void removeToken(String token) {
		secretMap.remove(token);
		authoriyMap.remove(token);
	}

	/**
	 * 清理缓存
	 */
	public void cleanTokenMap() {
		Set<String> set = secretMap.keySet();
		for (String token : set) {
			Claims claims = secretMap.get(token);
			if (claims == null) {
				secretMap.remove(token);
				authoriyMap.remove(token);
			} else {
				Date expiredDate = claims.getExpiration();
				if (expiredDate.before(new Date())) {
					secretMap.remove(token);
					authoriyMap.remove(token);
				}
			}
		}
	}

	/**
	 * 根据负责生成JWT的token
	 */
	private String generateToken(Map<String, Object> claims) {
		return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
	}

	/**
	 * 从token中获取JWT中的负载
	 */
	private Claims getClaimsFromToken(String token) {
		Claims claims = secretMap.get(token);
		if (claims != null) {
			return claims;
		}
		try {
			claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
		} catch (Exception e) {
			log.info("JWT格式验证失败:{}", token);
			secretMap.remove(token);
			authoriyMap.remove(token);
			throw new CommonException(TokenError.tokenError);
		}
		return claims;
	}

	/**
	 * 生成token的过期时间
	 */
	private Date generateExpirationDate() {
		return new Date(System.currentTimeMillis() + getExpiration() * 1000);
	}

	/**
	 * 根据参数获取超时时间,默认为24小时
	 * @return
	 */
	private Long getExpiration() {
		if(expiration!=null) {
			return expiration;
		}
		return expiration;
	}
	
}
